#!/bin/bash
set -e

# 基础
export JAVA_HOME=/usr/local/java/jdk1.8.0_321
export PATH=PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

DATE=$(date +%Y%m%d%H%M)
# 基础路径
BASE_PATH=/work/projects/jenkins-demo
# 编译后 jar 的地址。部署时，Jenkins 会上传 jar 包到该目录下
SOURCE_PATH=$BASE_PATH/build
# 服务名称。同时约定部署服务的 jar 包名字也为它。
SERVER_NAME=jenkins
# 环境
PROFILES_ACTIVE=$1
if [ -z "${PROFILES_ACTIVE}" ]; then
  PROFILES_ACTIVE=prod
fi
# 健康检查 URL
HEALTH_CHECK_URL=http://127.0.0.1:8081/actuator/health/
# 修改状态 URL
STATUS_URL=http://127.0.0.1:8081/actuator/serviceregistry/

# heapError 存放路径
HEAP_ERROR_PATH=$BASE_PATH/heapError
# JVM 参数
JAVA_OPS="-Xms256m -Xmx256m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$HEAP_ERROR_PATH"
# JavaAgent 参数。可用于配置 SkyWalking 等链路追踪
JAVA_AGENT=

# 备份
function backup() {
  # 如果不存在，则无需备份
  if [ ! -f "$BASE_PATH/$SERVER_NAME.jar" ]; then
    echo "[backup] $BASE_PATH/$SERVER_NAME.jar 不存在，跳过备份"
  # 如果存在，则备份到 backup 目录下，使用时间作为后缀
  else
    if [ ! -d "$BASE_PATH/backup/" ]; then
      echo "[backup] 创建备份目录 $BASE_PATH/backup"
      mkdir $BASE_PATH/backup
    fi
    echo "[backup] 开始备份 $SERVER_NAME ..."
    cp $BASE_PATH/$SERVER_NAME.jar $BASE_PATH/backup/$SERVER_NAME-$DATE.jar
    echo "[backup] 备份 $SERVER_NAME 完成"
  fi
}

# 最新构建代码 移动到项目环境
function transfer() {
  echo "[transfer] 开始转移 $SERVER_NAME.jar"

  # 删除原 jar 包
  if [ ! -f "$BASE_PATH/$SERVER_NAME.jar" ]; then
    echo "[transfer] $BASE_PATH/$SERVER_NAME.jar 不存在，跳过删除"
  else
    echo "[transfer] 移除 $BASE_PATH/$SERVER_NAME.jar 完成"
    rm $BASE_PATH/$SERVER_NAME.jar
  fi

  # 复制新 jar 包
  echo "[transfer] 从 $SOURCE_PATH 中获取 $SERVER_NAME.jar 并迁移至 $BASE_PATH ...."
  cp $SOURCE_PATH/$SERVER_NAME.jar $BASE_PATH

  echo "[transfer] 转移 $SERVER_NAME.jar 完成"
}

# 停止
function stop() {
  echo "[stop] 开始停止 $BASE_PATH/$SERVER_NAME"
  PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v "grep" | awk '{print $2}')
  # 如果 Java 服务启动中，则进行关闭
  if [ -n "$PID" ]; then
    # 从注册中心下线
    echo "[stop] 从注册中心下线当前实例，并 sleep 20 秒"
    curl -X POST $STATUS_URL -d '{"status": "DOWN"}' -H 'content-type: application/json'
    sleep 20
    # 正常关闭
    echo "[stop] $BASE_PATH/$SERVER_NAME 运行中，开始 kill [$PID]"
    kill -15 $PID
    # 等待最大 60 秒，直到关闭完成。
    for ((i = 0; i < 60; i++)); do
      sleep 1
      PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v "grep" | awk '{print $2}')
      if [ -n "$PID" ]; then
        echo -e ".\c"
      else
        echo "[stop] 停止 $BASE_PATH/$SERVER_NAME 成功"
        break
      fi
    done

    # 如果正常关闭失败，那么进行强制 kill -9 进行关闭
    if [ -n "$PID" ]; then
      echo "[stop] $BASE_PATH/$SERVER_NAME 失败，强制 kill -9 $PID"
      kill -9 $PID
    fi
  # 如果 Java 服务未启动，则无需关闭
  else
    echo "[stop] $BASE_PATH/$SERVER_NAME 未启动，无需停止"
  fi
}

# 启动
function start() {
  # 开启启动前，打印启动参数
  echo "[start] 开始启动 $BASE_PATH/$SERVER_NAME"
  echo "[start] JAVA_OPS: $JAVA_OPS"
  echo "[start] JAVA_AGENT: $JAVA_AGENT"
  echo "[start] PROFILES: $PROFILES_ACTIVE"

  # 开始启动
  BUILD_ID=dontKillMe nohup java -server $JAVA_OPS $JAVA_AGENT -jar $BASE_PATH/$SERVER_NAME.jar --spring.profiles.active=$PROFILES_ACTIVE &
  echo "[start] 启动 $BASE_PATH/$SERVER_NAME 完成"
}

# 健康检查
function healthCheck() {
  # 如果配置健康检查，则进行健康检查
  if [ -n "$HEALTH_CHECK_URL" ]; then
    # 健康检查最大 60 秒，直到健康检查通过
    echo "[healthCheck] 开始通过 $HEALTH_CHECK_URL 地址，进行健康检查"
    for ((i = 0; i < 60; i++)); do
      # 请求健康检查地址，只获取状态码。
      result=$(curl -I -m 10 -o /dev/null -s -w %{http_code} $HEALTH_CHECK_URL || echo "000")
      # 如果状态码为 200，则说明健康检查通过
      if [ "$result" == "200" ]; then
        echo "[healthCheck] 健康检查通过"
        break
      # 如果状态码非 200，则说明未通过。sleep 1 秒后，继续重试
      else
        echo -e ".\c"
        sleep 1
      fi
    done

    # 健康检查未通过，则异常退出 shell 脚本，不继续部署。
    if [ ! "$result" == "200" ]; then
      echo "[healthCheck] 健康检查不通过，可能部署失败。查看日志，自行判断是否启动成功"
      tail -n 30 nohup.out
      exit 1
    # 健康检查通过，打印最后 30 行日志，可能部署的人想看下日志。
    else
      tail -n 30 nohup.out
    fi
  # 如果未配置健康检查，则 slepp 60 秒，人工看日志是否部署成功。
  else
    echo "[healthCheck] HEALTH_CHECK_URL 未配置，开始 sleep 60 秒"
    sleep 60
    echo "[healthCheck] sleep 60 秒完成，查看日志，自行判断是否启动成功"
    tail -n 50 nohup.out
  fi
}

# 部署
function deploy() {
  cd $BASE_PATH
  # 备份原 jar
  backup
  # 停止 Java 服务
  stop
  # 部署新 jar
  transfer
  # 启动 Java 服务
  start
  # 健康检查
  healthCheck
}

deploy
